SuperStrict

Rem
bbdoc: String buffer
End Rem
Module Otus.StrBuf

ModuleInfo "Version: 1.00"
ModuleInfo "Author: Jan Varho"
ModuleInfo "License: Public domain"

Rem
bbdoc: A mutable string buffer
End Rem
Type TStrBuf
	
	Const INITIAL_CAPACITY% = 16
	Const CHARACTER_WIDTH% = 2
	
	' Current capacity
	Field _cap%
	
	' Internal buffer
	Field _buf:Short Ptr
	
	Rem
	bbdoc: Length of the buffer
	about: Read only!
	End Rem
	Field length%
	
	Rem
	bbdoc: Append a string to the buffer
	End Rem
	Method Append(s:String)
		Local l% = length + s.length
		If l > _cap Then _resize l
		
		For Local i% = 0 Until s.length
			_buf[length+i] = s[i]
		Next
		
		length = l
	End Method
	
	Rem
	bbdoc: Append another buffer to the buffer
	End Rem
	Method AppendBuf(s:TStrBuf)
		Local l% = length + s.length
		If l > _cap Then _resize l
		
		For Local i% = 0 Until s.length
			_buf[length+i] = s._buf[i]
		Next
		
		length = l
	End Method
	
	Rem
	bbdoc: Removes a range from the buffer
	about: Indices must be in 0..length
	End Rem
	Method Remove(s%, e%)
		If s < 0 Or e > length Or e < s Then RuntimeError "Index out of range"
		
		If e < length Then MemMove _buf+s, _buf+e, CHARACTER_WIDTH*(length-e)
		
		length :- (e - s)
	End Method
	
	Rem
	bbdoc: Replaces a range with a string
	about: Indices must be in 0..length
	End Rem
	Method Replace(s%, e%, str:String)
		If s < 0 Or e > length Or e < s Then RuntimeError "Index out of range"
		
		Local l% = length + s - e + str.length
		If l > _cap Then _resize l
		
		If e < length Then MemMove _buf+s+str.length, _buf+e, CHARACTER_WIDTH*(length-e)
		
		For Local i% = 0 Until str.length
			_buf[s+i] = str[i]
		Next
		
		length = l
	End Method
	
	Rem
	bbdoc: Insert a string into the buffer
	about: Index must be in 0..length
	End Rem
	Method Insert(index%, s:String)
		If index < 0 Or index > length Then RuntimeError "Index out of range"
		
		Local l% = length + s.length
		If l > _cap Then _resize l
		
		If index < length..
			MemMove _buf+index+s.length, _buf+index, CHARACTER_WIDTH*(length-index)
		
		For Local i% = 0 Until s.length
			_buf[index+i] = s[i]
		Next
		
		length = l
	End Method
	
	Rem
	bbdoc: Insert another buffer into the buffer
	about: Index must be in 0..length
	End Rem
	Method InsertBuf(index%, s:TStrBuf)
		If index < 0 Or index > length Then RuntimeError "Index out of range"
		
		Local l% = length + s.length
		If l > _cap Then _resize l
		
		If index < length..
			MemMove _buf+index+s.length, _buf+index, CHARACTER_WIDTH*(length-index)
		
		For Local i% = 0 Until s.length
			_buf[index+i] = s._buf[i]
		Next
		
		length = l
	End Method
	
	Rem
	bbdoc: Returns a buffer for a subrange
	about: Indices must be in 0..length
	End Rem
	Method SubBuf:TStrBuf(s%, e%)
		If s < 0 Or e > length Or e < s Then RuntimeError "Index out of range"
		
		Local b:TStrBuf = New TStrBuf
		
		b.length = e-s
		b._resize b.length
		MemCopy b._buf, _buf+s, CHARACTER_WIDTH*b.length
		
		Return b
	End Method
	
	Rem
	bbdoc: Returns a string for a subrange
	about: Indices must be in 0..length
	End Rem
	Method SubStr:String(s%, e%)
		If s < 0 Or e > length Or e < s Then RuntimeError "Index out of range"
		
		Return String.FromShorts(_buf+s, e-s)
	End Method
	
	Rem
	bbdoc: Create a C-String from the buffer
	about:
	The returned memory must eventually 
	be freed with a call to MemFree.
	End Rem
	Method ToCString:Byte Ptr()
		Local str:Byte Ptr = MemAlloc(length+1)
		
		For Local i% = 0 Until length
			str[i] = _buf[i]
		Next
		str[length] = 0
		
		Return str
	End Method
	
	Rem
	bbdoc: Create a W-String from the buffer
	about:
	The returned memory must eventually 
	be freed with a call to MemFree.
	End Rem
	Method ToWString:Short Ptr()
		Local str:Short Ptr = Short Ptr(MemAlloc(CHARACTER_WIDTH*(length+1)))
		
		For Local i% = 0 Until length
			str[i] = _buf[i]
		Next
		str[length] = 0
		
		Return str
	End Method
	
	Rem
	bbdoc: Create a String from the buffer
	End Rem
	Method ToString:String()
		Return String.FromShorts(_buf, length)
	End Method
	
	Method Delete()
		If _cap Then MemFree _buf
	End Method
	
	Method _resize(l:Int)
		Local c% = _cap
		If Not c Then c = INITIAL_CAPACITY
		
		While l > c
			c :Shl 1
		Wend
		
		If _cap
			_buf = Short Ptr(MemExtend(_buf, CHARACTER_WIDTH*_cap, CHARACTER_WIDTH*c))
		Else
			_buf = Short Ptr(MemAlloc(CHARACTER_WIDTH*c))
		End If
		
		_cap = c
	End Method
	
End Type
